package com.tuniu.agents.tools.flights.domestic.oneway;

import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import com.tuniu.agents.advisor.ChatEnvironmentMemory;
import com.tuniu.agents.message.MessageSender;
import com.tuniu.agents.message.ToolLogger;
import com.tuniu.agents.message.ToolLoggerInfo;
import com.tuniu.agents.tools.flights.FlightProperties;
import com.tuniu.agents.tools.flights.enums.FlightToolEnum;
import com.tuniu.agents.tools.flights.model.DetailModel;
import com.tuniu.agents.tools.flights.model.FlightMpParam;
import com.tuniu.agents.tools.flights.model.Url;
import com.tuniu.agents.tools.flights.utils.AirportQueryUtils;
import com.tuniu.agents.tools.flights.utils.CityCodeUtils;
import com.tuniu.agents.tools.flights.utils.FlightCommonUtils;
import com.tuniu.agents.tools.flights.utils.FlightMessageUtils;
import com.tuniu.agents.util.HttpManager;
import com.tuniu.agents.util.I18nUtil;
import com.tuniu.agents.util.JsonUtil;
import com.tuniu.agents.util.WebClientManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.model.ToolContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.stream.Collectors;

public class FlightLowPriceQueryTool implements BiFunction<FlightLowPriceQueryTool.Request, ToolContext, FlightLowPriceQueryTool.Response>, ToolLogger {

    public static final String TOOLNAME = "FlightLowPriceQueryTool";

    private static final Logger logger = LoggerFactory.getLogger(FlightLowPriceQueryTool.class);

    private final String flightLowPriceQueryToolUrl;
    private final String flightNearGoQueryToolUrl;
    private final String flightNearBackQueryToolUrl;

    private final String approachApiUrl;

    @Autowired(required = false)
    private ChatEnvironmentMemory chatEnvironmentMemory;

    @Autowired
    private HttpManager httpManager;

    @Autowired
    private MessageSender messageSender;

    public FlightLowPriceQueryTool(FlightProperties properties) {
        this.flightLowPriceQueryToolUrl = properties.getFlightLowPriceQueryToolUrl();
        this.flightNearGoQueryToolUrl = properties.getFlightNearGoQueryToolUrl();
        this.flightNearBackQueryToolUrl = properties.getFlightNearBackQueryToolUrl();
        this.approachApiUrl = properties.getApproachQueryToolUrl();
    }

    @ToolLoggerInfo(beanName = TOOLNAME)
    @Override
    public Response apply(Request request, ToolContext context) {
        try {
            // Get the custom parameters corresponding to the session
            Map<String, Object> map = context.getContext();
            String conversationId = (String) map.get("conversation_id");
            logger.info("FlightLowPriceQueryTool request parameters: {}", request);
            Response post = null;
            List<String> cityList = new ArrayList<>();
            cityList.add(request.departureCityName);
            cityList.add(request.arrivalCityName);
            Map<String, Boolean> mapCity = AirportQueryUtils.batchCheckAirports(httpManager, approachApiUrl, cityList);
            String[] loadingStr = FlightToolEnum.FLIGHT_LOW_PRICE_LIST.getInner().split(",");
            if (mapCity.get(request.departureCityName)) {
                messageSender.send(conversationId, I18nUtil.getMessage(conversationId,loadingStr[0]));
                post = httpManager.post(flightNearGoQueryToolUrl, request, Response.class);
                logger.info("flightApiUrl:{}", flightNearGoQueryToolUrl);
            } else if (mapCity.get(request.arrivalCityName)) {
                messageSender.send(conversationId, I18nUtil.getMessage(conversationId,loadingStr[1]));
                post = httpManager.post(flightNearBackQueryToolUrl, request, Response.class);
                logger.info("flightApiUrl:{}", flightNearBackQueryToolUrl);
            } else {
                post = httpManager.post(flightLowPriceQueryToolUrl, request, Response.class);
                logger.info("flightApiUrl:{}", flightLowPriceQueryToolUrl);
            }
            if (post.queryId != null) {
                Map<String, Object> hashMap = chatEnvironmentMemory.get((String) map.get("conversation_id"));
                hashMap.put("queryId", post.queryId);
                hashMap.put("departureCityName", request.departureCityName);
                hashMap.put("arrivalCityName", request.arrivalCityName);
                hashMap.put("departureDate", request.departureDate);
                hashMap.put("backDepartureDate", "");
                chatEnvironmentMemory.add((String) map.get("conversation_id"), hashMap);
            }
            return post.buildResponse();
        } catch (Exception e) {
            logger.error("FlightLowPriceQueryTool request exception: {}", e);
            return null;
        }
    }

    @Override
    public List<String> before(Object request, ToolContext context) {
        Request rRequest = (Request) request;
        String conversationId = (String) context.getContext().get("conversation_id");
        if (StringUtils.hasText(rRequest.departureCityName) && StringUtils.hasText(rRequest.arrivalCityName) && StringUtils.hasText(rRequest.departureDate)) {
            return List.of(FlightMessageUtils.getShowMessage(rRequest.departureCityName, rRequest.arrivalCityName, rRequest.departureDate, null,conversationId),
                    I18nUtil.getMessage(conversationId, FlightToolEnum.FLIGHT_LOW_PRICE_LIST.getBefore()));
        }
        return null;
    }

    @Override
    public List<String> after(Object request, ToolContext context, Object response) {
        String conversationId = (String) context.getContext().get("conversation_id");
        Response result = (Response) response;
        if (response != null && result.data() != null) {
            return List.of(FlightMessageUtils.getToolMessage(result.data().size(), conversationId));
        }
        return null;
    }

    @JsonClassDescription("国内机票单程列表查询入参")
    public record Request(
            @JsonProperty(value = "departureCityName", required = true) @JsonPropertyDescription("出发城市，出发城市和到达城市不可为同一个") String departureCityName,
            @JsonProperty(value = "arrivalCityName", required = true) @JsonPropertyDescription("到达城市，出发城市和到达城市不可为同一个") String arrivalCityName,
            @JsonProperty(value = "departureDate", required = true) @JsonPropertyDescription("出发日期，格式:yyyy-MM-dd") String departureDate) {
    }

    @JsonClassDescription("国内机票单程列表查询返参")
    public record Response(
            @JsonProperty(value = "success") @JsonPropertyDescription("成功标识，成功：\"查询成功！\"，失败：\"查询失败！\"") String success,
            @JsonProperty(value = "queryId") @JsonPropertyDescription("查询快照ID") String queryId,
            @JsonProperty(value = "data") @JsonPropertyDescription("返回国内航班数据") List<FlightItem> data) {

        private String convertTimeFormat(String time) {
            if (StringUtils.hasText(time)) {
                return time.replace("m", "分").replace("h", "时");
            }
            return time;
        }

        public Response buildResponse() {
            List<FlightItem> flightItems = this.data.stream().map(flightItem -> {
                return new FlightItem(
                        flightItem.airlineCompany,
                        flightItem.arrivalAirport,
                        flightItem.arrivalTime,
                        flightItem.arrivalTerminal,
                        flightItem.cabinClass,
                        flightItem.craftType,
                        flightItem.planModel,
                        flightItem.departureAirport,
                        flightItem.departureTime,
                        flightItem.departureTerminal,
                        flightItem.flightNumber,
                        flightItem.shareCode,
                        flightItem.priceWithTax,
                        flightItem.remainingSeats,
                        flightItem.type,
                        flightItem.flyTime,
                        convertTimeFormat(flightItem.totalDuration),
                        flightItem.stopPoint,
                        flightItem.transferPoint,
                        convertTimeFormat(flightItem.stopTime),
                        convertTimeFormat(flightItem.transferTime),
                        flightItem.airComImageUrl,
                        flightItem.url,
                        flightItem.departureCityName,
                        flightItem.arrivalCityName
                );
            }).toList();

            return new Response(
                    this.success,
                    this.queryId,
                    flightItems
            );
        }
    }

    @JsonClassDescription("航班列表")
    public record FlightItem(
            @JsonProperty(value = "airlineCompany") @JsonPropertyDescription("航司") String airlineCompany,
            @JsonProperty(value = "arrivalAirport") @JsonPropertyDescription("到达机场") String arrivalAirport,
            @JsonProperty(value = "arrivalTime") @JsonPropertyDescription("到达时间") String arrivalTime,
            @JsonProperty(value = "arrivalTerminal") @JsonPropertyDescription("到达航站楼") String arrivalTerminal,
            @JsonProperty(value = "cabinClass") @JsonPropertyDescription("舱等") String cabinClass,
            @JsonProperty(value = "craftType") @JsonPropertyDescription("机型") String craftType,
            @JsonProperty(value = "planModel") @JsonPropertyDescription("机型大小") String planModel,
            @JsonProperty(value = "departureAirport") @JsonPropertyDescription("出发机场") String departureAirport,
            @JsonProperty(value = "departureTime") @JsonPropertyDescription("出发时间") String departureTime,
            @JsonProperty(value = "departureTerminal") @JsonPropertyDescription("出发航站楼") String departureTerminal,
            @JsonProperty(value = "flightNumber") @JsonPropertyDescription("航班号") String flightNumber,
            @JsonProperty(value = "shareCode") @JsonPropertyDescription("实际承运航班号") String shareCode,
            @JsonProperty(value = "priceWithTax") @JsonPropertyDescription("成人价格") String priceWithTax,
            @JsonProperty(value = "remainingSeats") @JsonPropertyDescription("剩余位置") String remainingSeats,
            @JsonProperty(value = "type") @JsonPropertyDescription("航班类型：直飞，中转，联程，经停") String type,
            @JsonProperty(value = "flyTime") @JsonPropertyDescription("航班飞行时长") String flyTime,
            @JsonProperty(value = "totalDuration") @JsonPropertyDescription("总时长") String totalDuration,
            @JsonProperty(value = "stopPoint") @JsonPropertyDescription("经停城市") String stopPoint,
            @JsonProperty(value = "transferPoint") @JsonPropertyDescription("中转城市") String transferPoint,
            @JsonProperty(value = "stopTime") @JsonPropertyDescription("经停时长") String stopTime,
            @JsonProperty(value = "transferTime") @JsonPropertyDescription("中转时长") String transferTime,
            @JsonProperty(value = "airComImageUrl") @JsonPropertyDescription("航司图标链接") List<String> airComImageUrl,
            @JsonProperty(value = "url") @JsonPropertyDescription("收藏链接") Url url,
            @JsonProperty(value = "departureCityName") @JsonPropertyDescription("出发城市") String departureCityName,
            @JsonProperty(value = "arrivalCityName") @JsonPropertyDescription("到达城市") String arrivalCityName) {

        public FlightItem buildFlightItem(Url url, String departureCityName, String arrivalCityName) {
            return new FlightItem(
                    this.airlineCompany,
                    this.arrivalAirport,
                    this.arrivalTime,
                    this.arrivalTerminal,
                    this.cabinClass,
                    this.craftType,
                    this.planModel,
                    this.departureAirport,
                    this.departureTime,
                    this.departureTerminal,
                    this.flightNumber,
                    this.shareCode,
                    this.priceWithTax,
                    this.remainingSeats,
                    this.type,
                    this.flyTime,
                    this.totalDuration,
                    this.stopPoint,
                    this.transferPoint,
                    this.stopTime,
                    this.transferTime,
                    this.airComImageUrl,
                    url,
                    departureCityName,
                    arrivalCityName
            );
        }
    }

    public static Map<String, Object> buildCard(String flightsJson, Map<String, Object> hashMap) {
        if (flightsJson == null || flightsJson.isEmpty()) {
            return null;
        }

        Response response = JsonUtil.fromJson(flightsJson, Response.class);
        if (response == null) {
            return null;
        }
        HttpManager httpManager = new WebClientManager();
        //CityCodeUtils cityCodeUtils = new CityCodeUtils();
        CityCodeUtils.Ext departureCityExt = CityCodeUtils.getCityCode(httpManager, (String) hashMap.get("departureCityName"));
        CityCodeUtils.Ext arrivalCityExt = CityCodeUtils.getCityCode(httpManager, (String) hashMap.get("arrivalCityName"));
        return response.data.stream().map(flightItem -> {
            Url url = new Url();
            url.setAppUrl(buildUrl(flightItem, hashMap, departureCityExt, arrivalCityExt));
            url.setMpUrl(buildMpUrl(flightItem, hashMap, departureCityExt, arrivalCityExt));
            return flightItem.buildFlightItem(url, (String) hashMap.get("departureCityName"), (String) hashMap.get("arrivalCityName"));
        }).collect(Collectors.toMap(FlightItem::flightNumber, flightItem -> flightItem, (k1, k2) -> k2));
    }

    private static String buildUrl(FlightItem item,
                                   Map<String, Object> qParams,
                                   CityCodeUtils.Ext departureCityExt,
                                   CityCodeUtils.Ext arrivalCityExt) {
        // Domestic redirect link
        try {
            DetailModel model = new DetailModel();
            model.setQueryId((String) qParams.get("queryId"));
            model.setFlightNos(item.flightNumber);
            model.setDeptDate((String) qParams.get("departureDate"));
            model.convertAndSetCabinClass(item.cabinClass);
            model.setTransfer("中转".equals(item.type));

            model.setOrgCityName((String) qParams.get("departureCityName"));
            model.setOrgCityCode(departureCityExt.getCode());
            model.setOrgCityIataCode(departureCityExt.getIata_code());

            model.setDstCityName((String) qParams.get("arrivalCityName"));
            model.setDstCityCode(arrivalCityExt.getCode());
            model.setDstCityIataCode(arrivalCityExt.getIata_code());
            model.setSystemId(FlightCommonUtils.DOMESTIC_FLIGHT_SYSTEM_ID);

            return "tuniuapp://page?iosPageName=TNAirplaneDetailV2ViewController" +
                    "&androidPageName=com.tuniu.plane.activity.PlaneDomesticDetailActivity&pluginType=3&parameters="
                    + URLEncoder.encode(JsonUtil.toJson(model), StandardCharsets.UTF_8);

        } catch (Exception e) {
            logger.error("buildUrl exception: {} ", e.getMessage(), e);
            return null;
        }
    }

    private static String buildMpUrl(FlightItem item, Map<String, Object> qParams, CityCodeUtils.Ext departureCityExt, CityCodeUtils.Ext arrivalCityExt) {
        try {
            FlightMpParam mpParam = new FlightMpParam();
            mpParam.setQueryId((String) qParams.get("queryId"));
            mpParam.setFlightNos(item.flightNumber);
            mpParam.setDepartCityName((String) qParams.get("departureCityName"));
            mpParam.setArriveCityName((String) qParams.get("arrivalCityName"));
            mpParam.setIsSelectChild(0);
            mpParam.setIsSelectBaby(0);
            mpParam.setStartDate(item.departureTime);
            mpParam.setInitCityCode(departureCityExt.getCode());
            mpParam.setDestCityCode(arrivalCityExt.getCode());
            mpParam.setChildQuantity(0);
            mpParam.setBabyQuantity(0);
            mpParam.setDCityIataCode(departureCityExt.getIata_code());
            mpParam.setACityIataCode(arrivalCityExt.getIata_code());
            // Cabin class conversion
            List<String> cabinTypeList = convertCabinClass(item.cabinClass);
            mpParam.setSelectCabinTypes(cabinTypeList);
            mpParam.setCabinType(String.join("-", cabinTypeList));
            mpParam.setPriceFix(false);
            return "/flight/pages/flightdetail/index?params=" + URLEncoder.encode(JsonUtil.toJson(mpParam), StandardCharsets.UTF_8);
        } catch (Exception e) {
            logger.error("FlightLowPriceQueryTool buildMpUrl exception: {} ", e.getMessage(), e);
            return null;
        }
    }

    private static List<String> convertCabinClass(String cabinClasses) {
        List<String> cabinTypeList = new ArrayList<>();
        for (String cabinClass : cabinClasses.split("-")) {
            switch (cabinClass) {
                case "经济舱":
                    cabinTypeList.add("1");
                    break;
                case "公务舱":
                    cabinTypeList.add("2");
                    break;
                case "头等舱":
                    cabinTypeList.add("3");
                    break;
                case "超级经济舱":
                    cabinTypeList.add("4");
                    break;
            }
        }
        return cabinTypeList;
    }

    public static String buildMarkdown(String toolResponse) {
        Response response = JsonUtil.fromJson(toolResponse, Response.class);

        StringBuilder markdown = new StringBuilder();
        markdown.append("| Flight Key | 航班号 | 出发时间 | 到达时间 | 出发机场 | 到达机场 | 成人价格 | 剩余位置 | 总时长 | 航司 | 舱等 | 机型 | 航班类型 | 经停城市 | 经停时长 | 中转城市 | 中转时长 |\n");
        markdown.append("| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |\n");

        for (FlightItem flightItem : response.data()) {
            markdown.append("|").append(flightItem.flightNumber())
                    .append("|").append(flightItem.flightNumber())
                    .append("|").append(flightItem.departureTime())
                    .append("|").append(flightItem.arrivalTime())
                    .append("|").append(flightItem.departureAirport())
                    .append("机场|").append(flightItem.arrivalAirport())
                    .append("机场|").append(flightItem.priceWithTax())
                    .append("|").append(flightItem.remainingSeats())
                    .append("|").append(flightItem.totalDuration())
                    .append("|").append(flightItem.airlineCompany())
                    .append("|").append(flightItem.cabinClass())
                    .append("|").append(flightItem.craftType())
                    .append("|").append(flightItem.type())
                    .append("|").append(flightItem.stopPoint())
                    .append("|").append(flightItem.stopTime())
                    .append("|").append(flightItem.transferPoint())
                    .append("|").append(flightItem.transferTime())
                    .append("|\n");
        }
        markdown.append("\n");

        return markdown.toString();
    }
}